#include <string.h>
#include <stdio.h>
#include <utime.h>
#include <malloc.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

// Make RTPatch files from McAfee upd files

const char rtupd[]=".upd";
const char rtrtp[]=".rtp";

char * MakeNewName(char * name)
{
    char * newname=strdup(name);
    if (!newname) return 0;
    
    char * rt=strstr(newname,rtupd);
    if (!rt)
    {
	int len=strlen(newname);
	newname=(char*)realloc(newname,len+sizeof(rtrtp));
	if (!newname) return 0;
	rt=newname+len;
    }
    
    strcpy(rt,rtrtp);
    return newname;
}
#define Throw(n,f) {dbg_func=(f);dbg_line=__LINE__;throw(n);}
bool ExtractFile(char * name)
{
	struct stat statbuf;
	struct utimbuf ut;
	char * name_out=MakeNewName(name);
	if (!name_out) return false;
	int file_in=-1, file_out=-1;
	char * rdmap=(char*)-1, *wrmap=(char*)-1;
	unsigned long off, len, *dptr;
	bool res=true;

	char * dbg_func;
	int dbg_line;
	
	
	statbuf.st_atime=0;
	
	try
	{

//Open input file
	int file_in=open(name,O_RDONLY);
	if (file_in<0) Throw(1,"open")
	

//Just get some info about the file
	if (fstat(file_in,&statbuf) < 0) Throw(1,"fstat")
	
//Prepare Memory-Mapping on the file
	char * rdmap=(char*)mmap(0,statbuf.st_size,PROT_READ, MAP_SHARED, file_in, 0);
	if (rdmap == (char*) -1) Throw(1,"mmap")

//Let kernel knows - how to optimize page read-write on the file
	if (madvise(rdmap,statbuf.st_size,MADV_SEQUENTIAL)<0) Throw(1,"madvise")
	
	dptr=(unsigned long*)(rdmap+0x1BB);
	
	off=dptr[0];
	len=dptr[1];
//	fprintf(stderr,"%s:%s %lx:%lx [%lx]\n",name,name_out,off,len,statbuf.st_size);
			
	if (off>=statbuf.st_size || len>=statbuf.st_size ||
	    (off+len)>=statbuf.st_size) Throw(3,"check_offsets")

	int file_out=open(name_out,O_RDWR | O_CREAT | O_TRUNC,statbuf.st_mode);
	if (file_out<0) Throw(2,"open")
	
	if (lseek(file_out,len-1,SEEK_SET)!=len-1) Throw(2,"lseek")
	if (write(file_out,"",1) != 1) Throw(2,"write")
	
	char * wrmap=(char*)mmap(0,len,PROT_READ|PROT_WRITE, MAP_SHARED, file_out, 0);
	if (wrmap == (char*) -1) Throw(2,"mmap")
	
	if (madvise(wrmap,len,MADV_SEQUENTIAL)<0) Throw(2,"madvise")
	
	memcpy(wrmap,rdmap+off,len);


	if (munmap(rdmap,statbuf.st_size)<0) Throw(1,"munmap")
	rdmap=(char*)-1;
	
	if (munmap(wrmap,len)<0) Throw(2,"munmap")
	wrmap=(char*)-1;
	
	if (close(file_in)<0)  Throw(1,"close")
	file_in=-1;
	
	if (close(file_out)<0) Throw(2,"close")
	file_out=-1;

	ut.actime=statbuf.st_atime;
	ut.modtime=statbuf.st_mtime;
	if (utime(name_out,&ut)<0) Throw(2,"utime");
	
	} catch(int i) {
	    switch (i)
	    {
	    case 1:perror(name);break;
	    case 2:perror(name_out);break;
	    case 3:fprintf(stderr,"%s: wrong format or broken file\n",name);break;
	    }
	    fprintf(stderr,"In file %s:%d, %s()\n",__FILE__,dbg_line,dbg_func);
	    res=false;

	    if (rdmap != (char*) -1) munmap(rdmap,statbuf.st_size);
	    if (wrmap != (char*) -1) munmap(wrmap,len);
	    if (file_out>=0) close(file_out);
	    if (file_in>=0) close(file_in);
	}

	free(name_out);
	
// Make mtime alone
	return res;
}

int main(int n, char ** s)
{
	int count=0;
	while (*++s) if (ExtractFile(*s)) count++;
	return count;
}
